functor Mir2Asm (S : MIR2ASM_STRUCTS) : MIR2ASM = 
struct
  open S
  
  val localDebug = ref false

  structure M = Mir
  structure E = Env
  structure F = Frame
  structure A = F.Assem
  structure Aux4Mir = Aux4Mir(structure Mir = M)
  structure Tr = TransAsst (structure Env = E
                            structure Frame = F)
  structure Ctrl = Control
  structure D = DebugPrint
  structure T = Temp
  fun filt_jmp code =
      let fun filt [] = []
            | filt ((M.JMP(l))::code') = 
              (case code'
                of  (M.JMP(l'))::code'' => filt code'
                  |  _ => ((M.JMP(l))::(filt code')))
            | filt (i :: code') = i :: (filt code')
      in  List.rev(filt (List.rev code))
      end

  fun col_lb code = 
      let fun fdfun (M.JMP(lb), lblist) = lb::lblist
	    | fdfun (M.CJMP(cop, e1, e2, lb1, lb2), lblist) = [lb1, lb2]@lblist
	    | fdfun (t, lblist) = lblist
      in List.foldl fdfun [] code
      end

  fun del_lab code = 
      let fun ex (l::r) l1 = if l = l1
			     then true
			     else ex r l1
	    | ex [] l1 = false
	  val al_lb = col_lb code
	  val inRedunc = ref false 
	  fun cl (M.Label(lb, lbi)) = if ex al_lb lb
				      then (inRedunc := false;
					    [M.Label(lb, lbi)])
				      else (inRedunc := true;
					    [])
	    | cl t = if !inRedunc
		     then []
		     else [t]
      in List.concat(List.map cl code)
      end

  fun clean_mir mir = 
      let val mir' = filt_jmp mir
	  val mir'' = del_lab mir'
      in if mir = mir''
	 then mir
	 else clean_mir mir''
      end 

  fun ckMlab asmlist = 
      let fun newLb () = 
	      let val tlb = T.newLabel()
		  val opta = A.newAsst NONE
	      in A.Label(tlb, opta)
	      end
	  fun ckFn (inst, (postI, inss)) = 
	      case inst
	       of A.Jg(_) => if postI 
			     then (false, inst::inss)
			     else (false, [inst, newLb()]@inss)
		| A.Je(_) => if postI 
			     then (false, inst::inss)
			     else (false, [inst, newLb()]@inss)
		| A.Jge(_) => if postI 
			      then (false, inst::inss)
			      else (false, [inst, newLb()]@inss)
		| A.Jmp(_) => if postI 
			      then (false, inst::inss)
			      else (false, [inst, newLb()]@inss)
		| A.Call(_) => if postI 
			       then (false, inst::inss)
			       else (false, [inst, newLb()]@inss)
		| A.Ret(_) => if postI 
			      then raise Fail "bug\n"
			      else (false, inst::inss)
		| A.Malloc(_) => if postI 
				 then (false, inst::inss)
				 else (false, [inst, newLb()]@inss)
		| A.Label(_, _) => (true, inst::inss)
		| _ => (false, inst::inss)
      in #2(List.foldr ckFn (false, []) asmlist)
      end

  fun tr_off off = if off >= 0 
		   then Int.toString(off)
		   else "-"^Int.toString(0-off)
			
  fun sim_lval' (M.BINOP(M.PLUS, lval, M.CONST(i))) = 
      let val (node, off) = sim_lval' lval
      in (node, off+i)
      end
    | sim_lval' (M.NAME("_rev_ebp_")) = (M.NAME("_rev_ebp_"), 0)
    | sim_lval' (t) = (t, 0)

  fun sim_lval m = 
      let val (node, off) = sim_lval' m
      in M.BINOP(M.PLUS, node, M.CONST(off))
      end

  val C_env : ((bool * int * E.types) E.env) ref = ref (E.creat ())
  fun set_env env = C_env := env
 
  val C_senv : ((int * E.types) E.struct_env) ref= ref (E.creatse ())
  fun set_senv senv = C_senv := senv
  
  val C_rettp : bool ref = ref true 
  fun set_rettp (F.Ir.Int) = C_rettp := true
    | set_rettp (F.Ir.Bool) = C_rettp := true
    | set_rettp (F.Ir.Void) = C_rettp := true
    | set_rettp (F.Ir.Pointer t) = C_rettp := true
    | set_rettp _ = C_rettp := false
  fun isSimRet() = !C_rettp
  
  fun tr_aop M.PLUS = [A.Addr(A.Esi, A.Eax)]
    | tr_aop M.MINUS = [A.Subr(A.Esi, A.Eax)]
    | tr_aop M.MUL = [A.Mulr(A.Esi, A.Eax)]
    | tr_aop M.DIV = [A.Div(A.Esi)]
    | tr_aop M.MOD = [A.Div(A.Esi), A.Movr(A.Edx, A.Eax)]
    | tr_aop _ = []

  fun psh_arg' src treg init stp limit = 
      if init >= limit
      then []
      else [A.Movld((0-init), src, treg),A.Push(treg)]@(psh_arg' src treg (init + stp) stp limit)


  fun psh_arg (M.MEM(me1, size)) = if size > 4
				   then ((A.Addz(size-4, A.Eax))::(psh_arg' A.Eax A.Esi 0 4 size), size)
				   else ([A.Push(A.Eax)], 4)
    | psh_arg _ = ([A.Push(A.Eax)], 4)

  fun bk_copy (src, soff) treg (des,doff) init stp limit = 
      if init >= limit
      then []
      else ([(A.Movld(init+soff, src, treg)),(A.Movst(treg, init+doff, des))]@
	    (bk_copy (src, soff) treg (des,doff) (init + stp) stp limit))
  
  fun tr_lval' (M.BINOP(M.PLUS, M.NAME("_rev_ebp_"), M.CONST(off))) = ([], (A.Ebp, off))
    | tr_lval' (M.BINOP(M.PLUS, sub_lval, M.CONST(off))) = 
      let val (sub1', (bR, of1)) = tr_lval' sub_lval
      in (sub1', (bR, of1+off))
      end
    | tr_lval' (M.BINOP(M.PLUS, sub_lval, (M.BINOP(M.MUL, M.ARRAYEX(sub_exp, sz), M.CONST(elemSize))))) = 
      let val (sub1', (bR, of1)) = tr_lval' sub_lval
	  val sube' = tr_exp sub_exp 
	  val exasst = let fun asst (st0:A.state) (st1:A.state) = 
			       A.And(A.Ge(A.Sel(A.getRf(st1), A.Reg(A.Eax)), A.IntNum(0)),
				     A.Lt(A.Sel(A.getRf(st1), A.Reg(A.Eax)), A.IntNum(sz)))
		       in A.ExtrA(asst)
		       end
	  val n_sub1' = case bR
			 of A.Ebx => sube'@[exasst, A.Mulz(elemSize, A.Eax), A.Push(A.Eax)]@sub1'
				     @[A.Pop(A.Eax), A.Addz(of1, bR), A.Addr(A.Eax, bR)]
			  | A.Ebp => sube'@[exasst, A.Mulz(elemSize, A.Eax)]@sub1'
				     @[A.Movr(A.Ebp, A.Ebx), A.Addz(of1, A.Ebx), A.Addr(A.Eax, A.Ebx)]
			  | _ => raise Fail "bug\n"
      in (n_sub1', (A.Ebx, 0))
      end
    | tr_lval' (M.PTREX(sub_lval, sz)) = 
      let val (sub1', (bR, of1)) = tr_lval' sub_lval
	  fun exasst (br, ofst) = let fun asst (br, ofst) (st0:A.state) (st1:A.state) = 
					  A.Ne(A.Plus(A.Sel(A.getRf(st1), A.Reg(bR)), 
						      A.IntNum(ofst)), 
					       A.IntNum(0))
				  in A.ExtrA(asst (br, ofst))
				  end
	  val n_sub1' = sub1'@[exasst (bR, of1), A.Movld(of1, bR, A.Ebx)]
      in (n_sub1', (A.Ebx, 0))
      end 
    | tr_lval' (M.NAME(str)) = 
      (fn "_rev_ebp_" => ([], (A.Ebp, 0))
	| _ => ([A.Leal(str, A.Ebx)], (A.Ebx, 0))) str
    | tr_lval' t = (tr_exp t, (A.Ebx, 0))

  and tr_lval t = tr_lval' t
  and tr_exp (M.DUMMY) = []
    | tr_exp (M.CONST(i)) = [A.Movz(i, A.Eax, A.Int)]
    | tr_exp (M.PCONST(tp)) = 
      let fun trtp (F.Ir.Pointer(tp')) = A.Ptr(trtp(tp'))
	    | trtp (F.Ir.Int) = A.Int
	    | trtp (F.Ir.Struct(str)) = A.Str(str)
	    | trtp (F.Ir.Array(sz, tp)) = A.Wild
	    | trtp _ = A.Int
      in [A.Movz(0, A.Eax, (trtp tp))]
      end
    | tr_exp (M.NAME(str)) = (fn "_rev_ebp_" => [A.Movr(A.Ebp, A.Eax)]
			       | _ => [A.Leal(str, A.Eax)]) str
    | tr_exp (M.TEMP(str, preins)) = 
      let val pre' = List.concat(List.map tr_lnode preins)
      in pre'
      end
    | tr_exp (M.BINOP(M.PLUS, M.MEM(me1, s1), me2)) = 
      let val me2' = tr_exp me2
	  val (sub_me1', (bR, of1)) = tr_lval me1
      in case bR
	  of A.Ebx => sub_me1'@[A.Push(A.Ebx)]@me2'@[A.Pop(A.Ebx), A.Addm(of1, A.Ebx, A.Eax)]
	   | A.Ebp => me2'@[A.Addm(of1, A.Ebp, A.Eax)]
	   | _ => raise Fail "bug\n"
      end
    | tr_exp (M.BINOP(M.PLUS, me1, M.MEM(me2, s1))) = tr_exp (M.BINOP(M.PLUS, M.MEM(me2, s1), me1))
    | tr_exp (M.BINOP(M.MINUS, me1, M.MEM(me2, s1))) = 
      let val me1' = tr_exp me1
	  val (sub_me2', (bR, of1)) = tr_lval me2
      in case bR
	  of A.Ebx => sub_me2'@[A.Push(A.Ebx)]@me1'@[A.Pop(A.Ebx), A.Subm(of1, A.Ebx, A.Eax)]
	   | A.Ebp => me1'@[A.Subm(of1, A.Ebp, A.Eax)]
	   | _ => raise Fail "bug\n"
      end

    | tr_exp (M.BINOP(M.MUL, M.MEM(me1, s1), me2)) = 
      let val me2' = tr_exp me2
	  val (sub_me1', (bR, of1)) = tr_lval me1
      in case bR
	  of A.Ebx => sub_me1'@[A.Push(A.Ebx)]@me2'@[A.Pop(A.Ebx), A.Mulm(of1, A.Ebx, A.Eax)]
	   | A.Ebp => me2'@[A.Mulm(of1, A.Ebp, A.Eax)]
	   | _ => raise Fail "bug\n"
      end
    | tr_exp (M.BINOP(M.MUL, me1, M.MEM(me2, s1))) = tr_exp (M.BINOP(M.MUL, M.MEM(me2, s1), me1))
    | tr_exp (M.BINOP(maop, me1, me2)) = 
      let val me1' = tr_exp me1
	  val me2' = tr_exp me2
	  val maop' = tr_aop maop
      in me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi), A.Xchg(A.Eax, A.Esi)]@maop'
      end
    | tr_exp (M.MEM(me1, size)) = 
      let val (sub_me1', (bR, of1)) = tr_lval me1
      in if size > 4
	 then sub_me1'@[A.Movr(bR, A.Eax), A.Addz(of1, A.Eax)]
	 else sub_me1'@[A.Movld(of1, bR, A.Eax)]
      end
    | tr_exp (M.CALL(fid, palist)) = 
      let fun fdfun (pa, (intrs, argsize)) = 
	      let val (psh_ins, a_s) = psh_arg pa
	      in (intrs@(tr_exp pa)@(psh_ins), a_s+argsize)
	      end
          val (_, _, types) = Option.valOf(E.lookup fid (!C_env))
	  val tlb = T.newLabel()
	  fun freeFn [pa] = (tr_exp pa)@[A.Free(A.Eax)]
	    | freeFn _ = raise Fail "bug\n"

      in  if fid = "free"
	  then freeFn palist
	  else case types
		of  E.Simple => let val (ins, argsize) = List.foldr fdfun ([],0) palist
				in  ins@[A.Call(fid, tlb), A.Label(tlb, A.newAsst NONE), A.Addz(argsize, A.Esp)]
				end
		  | E.Pointer(subtype) =>  let val (ins, argsize) = List.foldr fdfun ([],0) palist
					   in  ins@[A.Call(fid, tlb), A.Label(tlb, A.newAsst NONE), A.Addz(argsize, A.Esp)]
					   end 
		  | _ => let val retsize = E.calTypeSize types (!C_senv)
                             val (ins, argsize) = List.foldr fdfun ([],0) palist
			 in  [(A.Subz (retsize, A.Esp)), (A.Leab (0, A.Esp, A.Edi))]@ins@
			     [(A.Push A.Edi), A.Call (fid, tlb), A.Label(tlb, A.newAsst NONE), (A.Addz (argsize+4+retsize, A.Esp))]
			 end
      end
    | tr_exp _ = []

  and tr_lnode (M.Label(str, lbinf)) =       
      if !Ctrl.doTrAsst
      then let val irAsstOpt = M.getLbInf lbinf
               val infunc = 0
               val env = !C_env
               val senv = !C_senv
               val asstOpt = Tr.top (NONE, irAsstOpt, infunc, env, senv)
           in  [A.Label(str, A.newAsst asstOpt)]
           end
      else [A.Label(str, A.newAsst NONE)]
    | tr_lnode (M.JMP(str)) = [A.Jmp(str)]
    | tr_lnode (M.EXP(me)) = tr_exp me
    | tr_lnode (M.MOV(M.NAME("_rev_ret_"), me1)) = 
      let val me1' = tr_exp me1
      in case me1
	  of (M.MEM(me, size)) => if size > 4 
				  then (me1'@[A.Movld (12, A.Ebp, A.Ebx)]@(bk_copy (A.Eax,0) A.Esi (A.Ebx, 0) 0 4 size)@
                                       [A.Movld (12, A.Ebp, A.Eax)])
				  else me1'
	   | _ => me1'
      end
    | tr_lnode (M.MOV(M.NAME(n), M.CONST(1))) = 
      if String.isPrefix "_T" n
      then [A.Movz(1, A.Eax, A.Int)]
      else (let val (me1', (bR, off)) = tr_lval (M.NAME(n))
		val me2' = tr_exp (M.CONST(1))
	    in me1'@me2'@[A.Movst(A.Eax, off, bR)]
	    end
	   )
    | tr_lnode (M.MOV(M.NAME(n), M.CONST(0))) = 
      if String.isPrefix "_T" n
      then [A.Movz(0, A.Eax, A.Int)]
      else let val (me1', (bR, off)) = tr_lval (M.NAME(n))
		val me2' = tr_exp (M.CONST(0))
	    in me1'@me2'@[A.Movst(A.Eax, off, bR)]
	    end
   
    | tr_lnode (M.MOV(me1, M.MEM(me2, size))) = 
      let val (me1', (bR1, of1)) = tr_lval me1
	  val (me2', (bR2, of2)) = tr_lval me2
	  val me_b = (fn (A.Ebp, A.Ebp) => [A.Movld(of2, A.Ebp, A.Eax), A.Movst(A.Eax, of1, A.Ebp)]
		       | (A.Ebx, A.Ebp) => me1'@[A.Movld(of2, A.Ebp, A.Eax), 
						 A.Movst(A.Eax, of1, A.Ebx)]
		       | (A.Ebp, A.Ebx) => me2'@[A.Movld(of2, A.Ebx, A.Eax), 
						 A.Movst(A.Eax, of1, A.Ebp)]
		       | (A.Ebx, A.Ebx) => me1'@[A.Push(A.Ebx)]@me2'
					   @[A.Pop(A.Eax), A.Movld(of2, A.Ebx, A.Ebx), A.Movst(A.Ebx, of1, A.Eax)]
		       | _ => raise Fail "bug\n"
		     ) (bR1, bR2)

	  val me_b' = (fn (A.Ebp, A.Ebp) => bk_copy (A.Ebp, of2) A.Eax (A.Ebp, of1) 0 4 size
			| (A.Ebx, A.Ebp) => me1'@(bk_copy (A.Ebp, of2) A.Eax (A.Ebx, of1) 0 4 size)
			| (A.Ebp, A.Ebx) => me2'@(bk_copy (A.Ebx, of2) A.Eax (A.Ebp, of1) 0 4 size)
			| (A.Ebx, A.Ebx) => me1'@[A.Push(A.Ebx)]@me2'
					   @[A.Pop(A.Eax)]@(bk_copy (A.Ebx, of2) A.Esi (A.Eax, of1) 0 4 size)
			| _ => raise Fail "bug\n"
		      ) (bR1, bR2)
      in if size = 4
	 then me_b
	 else me_b'
      end
    | tr_lnode (M.MOV(me1, M.CALL(fid, palist))) = 
      let val (me1', (bR, of1)) = tr_lval me1
          val (_, _, types) = Option.valOf(E.lookup fid (!C_env))
	  fun trtp (F.Ir.Pointer(tp')) = A.Ptr(trtp(tp'))
	    | trtp (F.Ir.Int) = A.Int
	    | trtp (F.Ir.Struct(str)) = A.Str(str)
	    | trtp (F.Ir.Array(sz, tp)) = A.Wild
	    | trtp _ = A.Int

	  fun getSz [M.CONST(sz), M.PCONST(tp)] = (sz, trtp tp)
	    | getSz _ = raise Fail "bug\n"

	  val me2' = if fid = "alloc"
		     then (let val (sz, tp) = getSz palist
			       val tlb = T.newLabel()
			       val szmd4 = Int.div(sz, 4)
			   in [A.Malloc(szmd4, tlb, tp), A.Label(tlb, A.newAsst NONE)]
			   end)
		     else tr_exp (M.CALL(fid, palist))

      in case types
          of  E.Simple => (fn A.Ebx => me2'@[A.Push(A.Eax)]@me1'@[A.Pop(A.Eax), A.Movst(A.Eax, of1, A.Ebx)]
			    | A.Ebp => me2'@ [A.Movst(A.Eax, of1, A.Ebp)]
			    | _  => raise Fail "bug\n") bR
	    | E.Pointer(subtype) => (fn A.Ebx => me2'@[A.Push(A.Eax)]@me1'@[A.Pop(A.Eax), A.Movst(A.Eax, of1, A.Ebx)]
				      | A.Ebp => me2'@ [A.Movst(A.Eax, of1, A.Ebp)]
				      | _  => raise Fail "bug\n") bR
	    | _ => let val size = E.calTypeSize types (!C_senv)
		   in (fn A.Ebx => me1'@[A.Push(A.Ebx)]@me2'@[A.Pop(A.Ebx)]@(bk_copy (A.Eax, 0) A.Esi (A.Ebx, of1) 0 4 size)
			| A.Ebp => me1'@me2'@(bk_copy (A.Eax, 0) A.Esi (A.Ebp, of1) 0 4 size)
			| _ => raise Fail "bug\n") bR
		   end
      end
    | tr_lnode (M.MOV(me1, me2)) = 
      let val (me1', (bR, of1)) = tr_lval me1
	  val me2' = tr_exp me2
      in case bR
	  of A.Ebx => me2'@[A.Push(A.Eax)]@me1'@[A.Pop(A.Eax), A.Movst(A.Eax, of1, A.Ebx)]
	   | A.Ebp => me2'@ [A.Movst(A.Eax, of1, A.Ebp)]
	   | _ => raise Fail "bug\n"
      end    
    | tr_lnode (M.CJMP(cop, me1, me2, tlb, flb)) = 
      let val me1' = tr_exp me1
	  val me2' = tr_exp me2
	  val tmplb = T.newLabel()
	  val cop' = (fn M.NE => [A.Cmpr(A.Esi, A.Eax), A.Je(flb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(tlb)]
		       | M.EQ => [A.Cmpr(A.Esi, A.Eax), A.Je(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
		       | M.LE => [A.Cmpr(A.Esi, A.Eax), A.Jge(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
		       | M.LT => [A.Cmpr(A.Esi, A.Eax), A.Jg(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
		       | M.GE => [A.Cmpr(A.Eax, A.Esi), A.Jge(tlb, tmplb),A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
		       | M.GT => [A.Cmpr(A.Eax, A.Esi), A.Jg(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]) cop

	  fun getimm (M.CONST(i)) = SOME(i)
	    | getimm (M.PCONST(tp)) = SOME(0)
	    | getimm _ = NONE

	  fun hdFn (M.NE) = (fn (NONE, NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i), NONE) => me2'@[A.Cmpz(i, A.Eax), A.Je(flb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(tlb)]
			       | (NONE, SOME(i)) => me1'@[A.Cmpz(i, A.Eax), A.Je(flb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(tlb)]
			       | (SOME(i1), SOME(i2)) => 
				 me1'@[A.Cmpz(i2, A.Eax), A.Je(flb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(tlb)]
			     ) (getimm(me1), getimm(me2))
	    | hdFn (M.EQ) = (fn (NONE, NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i), NONE) => me2'@[A.Cmpz(i, A.Eax), A.Je(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			       | (NONE, SOME(i)) => me1'@[A.Cmpz(i, A.Eax), A.Je(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			       | (SOME(i1), SOME(i2)) => 
				 me1'@[A.Cmpz(i2, A.Eax), A.Je(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			     ) (getimm(me1), getimm(me2))
	    | hdFn (M.LE) = (fn (NONE, NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i), NONE) => me2'@[A.Cmpz(i, A.Eax), A.Jge(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			       | (NONE, SOME(i)) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i1), SOME(i2)) => 
				 me2'@[A.Cmpz(i1, A.Eax), A.Jge(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			     ) (getimm(me1), getimm(me2))
	    | hdFn (M.LT) = (fn (NONE, NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i), NONE) => me2'@[A.Cmpz(i, A.Eax), A.Jg(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			       | (NONE, SOME(i)) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i1), SOME(i2)) => 
				 me2'@[A.Cmpz(i1, A.Eax), A.Jg(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			     ) (getimm(me1), getimm(me2))
	    | hdFn (M.GE) = (fn (NONE, NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i), NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (NONE, SOME(i)) => me1'@[A.Cmpz(i, A.Eax), A.Jge(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			       | (SOME(i1), SOME(i2)) => 
				 me1'@[A.Cmpz(i2, A.Eax), A.Jge(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			     ) (getimm(me1), getimm(me2))
	    | hdFn (M.GT) = (fn (NONE, NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (SOME(i), NONE) => me1'@[A.Push(A.Eax)]@me2'@[A.Pop(A.Esi)]@cop'
			       | (NONE, SOME(i)) => me1'@[A.Cmpz(i, A.Eax), A.Jg(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			       | (SOME(i1), SOME(i2)) => 
				 me1'@[A.Cmpz(i2, A.Eax), A.Jg(tlb, tmplb), A.Label(tmplb, A.newAsst NONE), A.Jmp(flb)]
			     ) (getimm(me1), getimm(me2))

      in hdFn cop
      end

  fun tr_data dlist = 
      let fun gendata (id, size) = 
              if size = 1
              then [A.Label (id, A.newAsst NONE), A.Directive ("\t.long 0")]
              else [A.Label (id, A.newAsst NONE), A.Directive ("\t.rept "^ Int.toString(size)), 
                    A.Directive ("\t.long 0"), A.Directive ("\t.endr")]
      in (A.Section(A.Data))::
         (A.Directive("\t.align 4"))::(List.concat(List.map gendata dlist))
      end 

  fun tr_fun (stmlist, frame, t_env, t_senv, prec, postc) = 
      let val fname = F.getFname frame
	  val ssize = (F.getLocals frame) - (F.getRegSize ())
	  val prolog = T.newLabel()
	  val epilog = F.getEpilog(frame)
	  val _ = set_env t_env
	  val _ = set_senv t_senv
          val rett = F.getRetType(frame)
	  val _ = set_rettp (rett)
          fun tr_frame () =
              if !Ctrl.doTrAsst
              then let val inf = 0
                       val outf1 = 1
                       val outf2 = 3
                       val _ = if !localDebug
                               then (D.debugPrint ("infunc_pre = "^(Int.toString outf1))
                                   ; D.debugPrint ("infunc_post = "^ (Int.toString outf2)))
                               else ()
                       val env = (!C_env)
                       val senv = (!C_senv) 
                       val preOut = Tr.top (SOME rett, SOME prec, outf1, env, senv)
                       val preIn = Tr.top (SOME rett, SOME prec, inf, env, senv)
                       val postOut = Tr.top (SOME rett, SOME postc, outf2, env, senv)
                       val postIn = Tr.top (SOME rett, SOME postc, inf, env, senv)
                       val newframe1 = F.setPre (frame, preOut)
                       val newframe2 = F.setPost (newframe1, postOut)
                       val hasRet = 1
                       val asmenv = Tr.topEnv (hasRet, env)
                       val newframe3 = F.setEnv (newframe2, asmenv)
                   in  (preOut, preIn, postIn, newframe3)
                   end
              else (NONE, NONE, NONE, frame)
          val (precOut, precIn, postcIn, newframe) = tr_frame ()
          fun debugPrintA apt =
              let val a = case apt
                           of  NONE => (D.showWarning "no assert and passert"; (fn s => fn s => A.True, NONE))
                             | SOME a' => a'
                  val a1 = #1 a
                  val a1str = "This is assertion."
                  val a2 = #2 a
                  val a2str = case a2 
                               of  NONE => "no passert"
                                 | SOME pa => A.pprop2str pa 
              in  D.debugPrint ("Assert: "^ a1str);
                  D.debugPrint ("Passert: "^a2str)
              end

          val _ = if !localDebug
                  then debugPrintA precOut
                  else ()
          val precIn' = case precIn
                         of  NONE => NONE
                           | SOME (a, pa) => SOME (a, NONE)
          val postcIn' = case postcIn
                          of  NONE => NONE
                            | SOME (a, pa) => SOME (a, NONE)
	  val pro1 = if fname = "main"
		     then (A.Label (fname, A.newAsstMain precOut))
		     else (A.Label (fname, A.newAsst precOut))
          val pro = pro1::[(A.Push A.Ebp),
			   (A.Movr (A.Esp,A.Ebp)),
			   (A.Push A.Ebx), 
			   (A.Push A.Esi),
			   (A.Push A.Edi),
			   (A.Subz (ssize,A.Esp)),
			   (A.Jmp prolog),
			   (A.Label (prolog, A.newAsst precIn'))]
          fun debugPrintB ins = 
              (case ins
                of  A.Label (id, opta) => 
                    (D.debugPrint ("The label is : "^ id)
                   ; D.debugPrint ("The label's ID is : "^ (Int.toString (A.getLabId opta)))
                   ; debugPrintA (A.getLabA opta))
                  | _ => D.showError "not a label")

          val _ = if !localDebug
                  then debugPrintB (List.hd pro)
                  else ()	 
	  val epi = [(A.Label (epilog, A.newAsst postcIn')),
		     (A.Addz (ssize, A.Esp)),
		     (A.Pop A.Edi),
		     (A.Pop A.Esi),
		     (A.Pop A.Ebx),
		     (A.Movr (A.Ebp, A.Esp)),
		     (A.Pop A.Ebp),
		     (A.Ret(fname))]

	  val t_mir1 = clean_mir stmlist
	  val t_mir' = Aux4Mir.aux_top t_mir1
	  val text' = List.concat(List.map tr_lnode t_mir1)
	  val text1 = pro@text'@epi
	  val text = ckMlab text1
      in (text, newframe)
      end

  fun tmp_out (t_mir, data) = 
      let fun itoString i = 
	      if (i>=0)
	      then Int.toString(i)
	      else ("-"^(Int.toString(Int.abs(i))))

	  fun tp2str (E.Simple) = "int"
	    | tp2str (E.Struct str) = "struct "^str
	    | tp2str (E.Array(d1, d2)) = "wild"
	    | tp2str (E.Pointer(tp)) = (tp2str tp)^"*"

	  fun envNode env' = 
	      let val env = E.toList env'
		  fun mapFn (vname, (isLocal, ofst, tp)) = 
		      if isLocal 
		      then "#\t"^(tp2str tp)^" "^itoString(ofst)^"(%ebp), "^
			   itoString(ofst-4)^"(%esp);\n"
		      else ""

	      in (fn [] => "#@\n#@\n"
		   | _ => "#@ \n"^(String.concat(List.map mapFn env))^"#@\n") env
	      end

	  fun senvNode senv = 
	      let fun mapFn1 (ofst, tp) = 
		      "\t"^itoString(ofst)^" : "^(tp2str tp)^";\n"

		  val senv' = E.transSenv (mapFn1, senv) 

		  fun mapFn2 (sname, t') = 
		      let val fields = ["{\n"]@t'@["}\n"]
			  val fields' = String.concat(List.map (fn t => "#"^t) fields)
		      in "#@ struct "^sname^"\n"^fields'^"#@\n\n"
		      end
	      in (fn [] => "#@\n#@\n"
		   | _ => String.concat(List.map mapFn2 senv')) senv'
	      end
  
	  val senvhd = (fn [] => raise Fail "mir list is empty!\n"
			 | t => let val (d1, d2, d3, senv, d4, d5) = List.hd(t)
				in senvNode senv
				end
		       ) t_mir

	  val hder = String.concat(List.map (A.toString) [(A.Section(A.Text)), (A.Directive("\t.align 4"))])

	  val funcRets : string list ref = ref []
	  fun addFuncR funName funRet = 
	      let fun rettp' tp = 
		      case tp 
		       of (F.Ir.Int) => "int"
			| (F.Ir.Bool) => "int"
			| (F.Ir.Void) => "wild"
			| (F.Ir.Pointer t) => (rettp' t)^"*"
			| (F.Ir.Struct str) => "struct "^str
			| _ => "wild"
		  val nStr = "#\t"^funName^" : "^(rettp' funRet)^";\n"
	      in funcRets := nStr::(!funcRets)
	      end

	  fun tr_fun' (t as (stmlist, frame, t_env, t_senv, prec, postc)) = 
	      let val fname = F.getFname frame
		  val frettp = F.getRetType frame
		  val _ = addFuncR fname frettp

		  val (asmlist', newframe) = tr_fun t
		  val envhd = envNode t_env
		  val p_prec = F.getPre newframe
		  val p_postc = F.getPost newframe

		  val p_prec' = (fn NONE => ""
				  | SOME(t1, t2) => let val t = (fn NONE => "true"
								  | SOME(tt) => (A.pprop2str tt)
								) t2
						    in t
						    end) p_prec
		  val p_postc' = (fn NONE => ""
				  | SOME(t1, t2) => let val t = (fn NONE => "true"
								  | SOME(tt) => (A.pprop2str tt)
								) t2
						    in t
						    end) p_postc
		  val prePost = "#@ pre = "^p_prec'^"\n#  post = "^p_postc'^"\n#@\n"
	      in envhd^prePost^
		 (String.concat(List.map (A.toString) ([(A.Directive ("\n\t.globl "^fname))]@asmlist')))
	      end

	  val assemblyfile = File.creat (String.concat [File.nameToString (!ErrorMsg.file), ".s"])


	  val data' = tr_data data

	  val funStr = String.concat(List.map tr_fun' t_mir)
	  val funRStr = "#@\n"^String.concat(!funcRets)^"#@\n"
	  val whole = senvhd^funRStr^hder^funStr
          val dataseg =String.concat(List.map (A.toString) data')

      in  File.write (assemblyfile, whole^dataseg)
      end 

  fun emitCode (t_mir, data) = 
      let fun tr_fun' (t as (stmlist, frame, t_env, t_senv, prec, postc)) = 
	      let val fname = F.getFname frame
		  val (asmlist', new_frame) = tr_fun t
	      in [(A.Directive ("\n\t.globl "^fname))]@asmlist'
	      end
	  val text = [(A.Section(A.Text)), (A.Directive("\t.align 4"))]@ 
		     List.concat (List.map tr_fun' t_mir)
	  val data = tr_data data
      in (text, data)
      end

  fun bdbb cds = 
      (fn (A.Label(t, opta)) => A.setCb(t, A.getLabId opta, List.tl(cds), A.getLabA opta, NONE, [])
 	| _ => raise Fail "bug\n"
      ) (List.hd(cds)) 

  fun genbb' (asm_instr, (codes, bblist)) = 
      let fun mtch (t as A.Jg(_)) = (fn [] => (t::codes, bblist)
				      | c => (let val newbb = bdbb c
					      in ([t], newbb::bblist)
					      end)) codes
	    | mtch (t as A.Je(_)) = (fn [] => (t::codes, bblist)
				      | c => (let val newbb = bdbb c
					      in ([t], newbb::bblist)
					      end)) codes
	    | mtch (t as A.Jge(_)) = (fn [] => (t::codes, bblist)
				      | c => (let val newbb = bdbb c
					      in ([t], newbb::bblist)
					      end)) codes
	    | mtch (t as A.Jmp(_)) = (fn [] => (t::codes, bblist)
				      | c => (let val newbb = bdbb c
					      in ([t], newbb::bblist)
					      end)) codes
	    | mtch (t as A.Call(_)) = (fn [] => (t::codes, bblist)
				      | c => (let val newbb = bdbb c
					      in ([t], newbb::bblist)
					      end)) codes
	    | mtch (t as A.Ret(_))  = (fn [] => (t::codes, bblist)
					| c => (let val newbb = bdbb c
						in ([t], newbb::bblist)
						end)) codes
	    | mtch (t as A.Malloc(_)) = (fn [] => (t::codes, bblist)
					  | c => (let val newbb = bdbb c
						  in ([t], newbb::bblist)
						  end)) codes
	    | mtch o_instr = (o_instr::codes, bblist)
      in mtch asm_instr
      end
  
  fun genbb asmlist fname = 
      let val (cds, bblist) = List.foldr genbb' ([],[]) asmlist
	  val entrybb : A.codeblk = bdbb cds
      in entrybb::bblist
      end

  fun emitCode' (t_mir, data) = 
      let fun foreach t_mir' = 
	      let val (asmlist, new_frame) = tr_fun t_mir'
		  val fname = F.getFname new_frame
		  val bbs = genbb asmlist fname
	      in F.setCode (new_frame, bbs)
	      end
          val senv = if !Ctrl.doTrAsst
                     then Tr.topSEnv (!C_senv)
                     else []
      in (List.map foreach t_mir, senv, tr_data data)
      end

end 
